#pragma once
#include <iostream>
#include <string>
#include <filesystem> //c++17的文件操作
#include <fstream>
#include <ctime>
#include <unistd.h>
#include <memory>
#include <sstream>
#include "mutex.hpp"
using namespace std;
// 枚举出来错误的类型

// enum class LogLevel
// {
//     DEBUG,
//     INFO,
//     WARNING,
//     ERRPR,
//     FATAL
// };

// string Level2String(LogLevel level)
// {
//     switch (level)
//     {
//     case LogLevel::DEBUG:
//         return "DEBBUG";
//     case LogLevel::INFO:
//         return "INFO";
//     case LogLevel::WARNING:
//         return "WARNING";
//     case LogLevel::ERRPR:
//         return "ERRPR";
//     case LogLevel::FATAL:
//         return "FATAl";
//     default:
//         return "UNKONW";
//     }
// }
// std::string GetCurrentTime()
// {
//     // 1. 获取时间戳
//     time_t currtime = time(nullptr);

//     // 2. 如何把时间戳转换成为20XX-08-04 12:27:03
//     struct tm currtm; // 返回一个结构体
//     localtime_r(&currtime, &currtm);

//     // 3. 转换成为字符串 -- dubug?
//     char timebuffer[64];
//     snprintf(timebuffer, sizeof(timebuffer), "%4d-%02d-%02d %02d:%02d:%02d",
//              currtm.tm_year + 1900, // 加1900 是因为默认从1900开始为1
//              currtm.tm_mon + 1,
//              currtm.tm_mday,
//              currtm.tm_hour,
//              currtm.tm_min,
//              currtm.tm_sec); // 格式化打印

//     return timebuffer;
// }
// class LogStrategy
// {
//     // C++11可以让你更好的控制要使⽤的默认函数。假设你要使⽤某个默认的函数，但是因为⼀些原因
//     //  这个函数没有默认⽣成。⽐如：我们提供了拷⻉构造，就不会⽣成移动构造了，那么我们可以使⽤
//     //  default关键字显⽰指定移动构造⽣成。
// public:
//     virtual ~LogStrategy() = default; // 这里的含义是 生成默认的析构函数 因为派生类析构的时候会调用基类的那部分
//     // 析构函数析构基类的那部分不然会报错
//     virtual void SyncLog(const std::string &logmessage) = 0;
//     // 后面的=0表示这是一个纯虚函数 包含至少一个纯虚函数的叫抽象类 抽象类不可以实例化 同时派生类必须实现这个虚函数 不让也会变成抽象类
// };
// // 显示器的刷新策略
// class ConsoleLogStrategy : public LogStrategy
// {
// public:
//     void SyncLog(const std::string &logmessage) override
//     {
//         {
//             lockgurd lockguard(&_lock);
//             std::cout << logmessage << std::endl;
//         }
//     }
//     ~ConsoleLogStrategy()
//     {
//     }

// private:
//     Mutex _lock;
// };
// // 以后改文件只要在这里修改就可以了
// const string logdefaultdir = "log";                // 文件目录
// const static std::string logfilename = "test.log"; // 文件目录

// // 文件刷新
// class FileLogStrategy : public LogStrategy
// {
// public:
//     FileLogStrategy(const string &dir = logdefaultdir, const string &filename = logfilename)
//         : _dir_path_name(dir), _filenmae(filename)
//     {
//         // 这段代码不是很理解，等复习完c++抛异常之后再来看
//         if (filesystem::exists(_dir_path_name))
//         {
//             return;
//         }
//         try
//         {
//             std::filesystem::create_directories(_dir_path_name);
//         }
//         catch (const std::filesystem::filesystem_error &e)
//         {
//             std::cerr << e.what() << '\n';
//         }
//     }
//     void SyncLog(const std::string &logmessage) override // override 检测虚函数是否需要重写
//     {
//         {
//             lockgurd lockguard(&_lock);
//             // 构建出文件目录
//             string target = _dir_path_name;
//             target += "/";
//             target += _filenmae;
//             // c_str() 返回的是一个 以 \0 结尾的 C 字符串，可直接传递给需要 const char* 的 C 函数（如 printf、strlen）。
//             ofstream out(target.c_str(), ios::app); // append 相当于以文件追加的方式打开
//             if (!out.is_open())
//             {
//                 return;
//             }
//             out << logmessage << "\n";
//             out.close();
//         }
//     }

// private:
//     Mutex _lock;
//     string _dir_path_name;
//     string _filenmae;
// };

// // 2.构建完整的日志
// class Logger
// {
// public:
//     Logger()
//     {
//     }
//     void EnableConsoleLogStrategy()
//     {
//         // 使用屏幕的刷新侧路 然后用基类的指针指向派生类
//         // 关于继承的指针问题。
//         // public继承的派生类可以赋值给基类的指针或者引用
//         _strategy = std::make_unique<ConsoleLogStrategy>();
//     }
//     void EnableFileLogStrategy()
//     {
//         _strategy = std::make_unique<FileLogStrategy>();
//     }
//     // 形成一条完整的日志
//     class Logmessage
//     {
//     public:
//         Logmessage(LogLevel level, string &filename, int line, Logger &logger)
//             : _curr_time(GetCurrentTime()),
//               _level(level),
//               _pid(getpid()),
//               _line(line),
//               _logger(logger)
//         {
//             stringstream ss;
//             ss << "[" << _curr_time << "] "
//                << "[" << Level2String(_level) << "] "
//                << "[" << _pid << "] "
//                << "[" << _filename << "] "
//                << "[" << _line << "]"
//                << " - ";
//             _loginfo = ss.str();
//         }
//         template <typename T>
//         Logmessage &operator<<(const T &info)
//         {
//             stringstream ss;
//             ss << info;
//             _loginfo += ss;
//             return *this
//         }
//         ~Logmessage()
//         {
//             if (_logger._strategy)
//             {
//                 _logger._strategy->SyncLog(_loginfo);
//             }
//         }

//     private:
//         string _curr_time; // 日志时间
//         LogLevel _level;   // 日志等级
//         pid_t _pid;
//         string _filename;
//         int _line; // 哪一行出现问题

//         std::string _loginfo; // 一条合并完成的，完整的日志信息
//         Logger &_logger;      // 提供刷新策略的具体做法
//     };
//     Logmessage operator()(LogLevel level, string filename, int line)
//     {
//         return Logmessage(level, filename, line, *this);
//     }
//     ~Logger()
//     {
        
//     }

// private:
//     // 智能指针不太理解 复习完再补充
//     unique_ptr<LogStrategy> _strategy;
// };
// Logger logger;
// #define LOG(level) logger(level, __FILE__, __LINE__)
// #define EnableConsoleLogStrategy() logger.EnableConsoleLogStrategy()
// #define EnableFileLogStrategy() logger.EnableFileLogStrategy()

// // __FILE__（当前文件名）
// // 预定义宏，由编译器自动替换为当前源文件的完整路径或文件名（字符串）。

// //  __LINE__（当前行号）
// // 预定义宏，由编译器自动替换为宏调用处的行号（整数）。


#pragma once

#include <iostream>
#include <string>
#include <filesystem> // C++17 文件操作
#include <fstream>
#include <ctime>
#include <unistd.h>
#include <memory>
#include <sstream>
#include "mutex.hpp"

// 规定出场景的日志等级
enum class LogLevel
{
    DEBUG,
    INFO,
    WARNING,
    ERROR,
    FATAL
};

std::string Level2String(LogLevel level)
{
    switch (level)
    {
    case LogLevel::DEBUG:
        return "Debug";
    case LogLevel::INFO:
        return "Info";
    case LogLevel::WARNING:
        return "Warning";
    case LogLevel::ERROR:
        return "Error";
    case LogLevel::FATAL:
        return "Fatal";
    default:
        return "Unknown";
    }
}

// 20XX-08-04 12:27:03
std::string GetCurrentTime()
{
    // 1. 获取时间戳
    time_t currtime = time(nullptr);

    // 2. 如何把时间戳转换成为20XX-08-04 12:27:03
    struct tm currtm;
    localtime_r(&currtime, &currtm);

    // 3. 转换成为字符串 -- dubug?
    char timebuffer[64];
    snprintf(timebuffer, sizeof(timebuffer), "%4d-%02d-%02d %02d:%02d:%02d",
             currtm.tm_year + 1900,
             currtm.tm_mon + 1,
             currtm.tm_mday,
             currtm.tm_hour,
             currtm.tm_min,
             currtm.tm_sec);

    return timebuffer;
}

///////////////////////////////////////////////////////////////////
// 1. 刷新的问题 -- 假设我们已经有了一条完整的日志，string->设备(显示器，文件)
// 基类方法
class LogStrategy
{
public:
    virtual ~LogStrategy() = default;
    virtual void SyncLog(const std::string &logmessage) = 0;
};

// 显示器刷新
class ConsoleLogStrategy : public LogStrategy
{
public:
    ~ConsoleLogStrategy()
    {
    }
    void SyncLog(const std::string &logmessage) override
    {
        {
            LockGuard lockguard(&_lock);
            std::cout << logmessage << std::endl;
        }
    }

private:
    Mutex _lock;
};

const std::string logdefaultdir = "log";
const static std::string logfilename = "test.log";

// 文件刷新
class FileLogStrategy : public LogStrategy
{
public:
    FileLogStrategy(const std::string &dir = logdefaultdir,
                    const std::string filename = logfilename)
        : _dir_path_name(dir), _filename(filename)
    {
        LockGuard lockguard(&_lock);
        if (std::filesystem::exists(_dir_path_name))
        {
            return;
        }
        try
        {
            std::filesystem::create_directories(_dir_path_name);
        }
        catch (const std::filesystem::filesystem_error &e)
        {
            std::cerr << e.what() << "\r\n";
        }
    }
    void SyncLog(const std::string &logmessage) override
    {
        {
            LockGuard lockguard(&_lock);
            std::string target = _dir_path_name;
            target += "/";
            target += _filename;

            std::ofstream out(target.c_str(), std::ios::app); // append
            if (!out.is_open())
            {
                return;
            }
            out << logmessage << "\n"; // out.write
            out.close();
        }
    }

    ~FileLogStrategy()
    {
    }

private:
    std::string _dir_path_name; // log
    std::string _filename;      // hello.log => log/hello.log
    Mutex _lock;
};

// 网络刷新

////////////////////////////////////////////////////////

// 1. 定制刷新策略
// 2. 构建完整的日志
class Logger
{
public:
    Logger()
    {
    }
    void EnableConsoleLogStrategy()
    {
        _strategy = std::make_unique<ConsoleLogStrategy>();
    }
    void EnableFileLogStrategy()
    {
        _strategy = std::make_unique<FileLogStrategy>();
    }
    // 形成一条完整日志的方式
    class LogMessage
    {
    public:
        LogMessage(LogLevel level, std::string &filename, int line, Logger &logger)
        : _curr_time(GetCurrentTime()),
          _level(level),
          _pid(getpid()),
          _filename(filename),
          _line(line),
          _logger(logger)
        {
            std::stringstream ss;
            ss << "[" << _curr_time << "] "
               << "[" << Level2String(_level) << "] "
               << "[" << _pid << "] "
               << "[" << _filename << "] "
               << "[" << _line << "]"
               <<  " - ";
            _loginfo = ss.str();
        }
        template<typename T>
        LogMessage& operator << (const T &info)
        {
            std::stringstream ss;
            ss << info;
            _loginfo += ss.str();
            return *this;
        }

        ~LogMessage()
        {
            if(_logger._strategy)
            {
                _logger._strategy->SyncLog(_loginfo);
            }
        }
    private:
        std::string _curr_time; // 日志时间
        LogLevel _level; // 日志等级
        pid_t _pid; // 进程pid
        std::string _filename;
        int _line;

        std::string _loginfo; // 一条合并完成的，完整的日志信息
        Logger &_logger; // 提供刷新策略的具体做法
    };
    LogMessage operator()(LogLevel level, std::string filename, int line)
    {
        return LogMessage(level, filename, line, *this);
    } 
    ~Logger()
    {
    }

private:
    std::unique_ptr<LogStrategy> _strategy;
};

Logger logger;

#define LOG(level) logger(level, __FILE__, __LINE__)
#define EnableConsoleLogStrategy() logger.EnableConsoleLogStrategy()
#define EnableFileLogStrategy() logger.EnableFileLogStrategy()